package sg.edu.nus.comp.cs4218.impl;

import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;
import java.util.StringTokenizer;

import sg.edu.nus.comp.cs4218.extended2.IWcTool;

public class WCTool extends ATool implements IWcTool {

	/**
	 * Constructor
	 * 
	 * @param args
	 *            - Arguments the tool is going to be executed with.
	 */
	public WCTool(String[] args) {
		super(args);
	}

	/**
	 * Calculate the number of characters in a file or from user input
	 * 
	 * @param input
	 *            - A string representation of the user input
	 * @return A string representation of the number of characters
	 */
	@Override
	public String getCharacterCount(String input) {
		int noOfChars = 0;
		String charCount = "";
		String[] strArray = input.split("\n");
		for (int i = 0; i < strArray.length; i++) {
			noOfChars += strArray[i].length();
		}
		setStatusCode(0);
		charCount = "" + noOfChars;
		return charCount;
	}

	/**
	 * Calculate the number of words in a file or from user input
	 * 
	 * @param input
	 *            - A string representation of the user input
	 * @return A string representation of the number of words
	 */
	@Override
	public String getWordCount(String input) {
		int noOfWords = 0;
		String wordCount = "";
		String[] strArray = input.split("\n");
		for (int i = 0; i < strArray.length; i++) {
			String line = strArray[i];
			noOfWords += new StringTokenizer(line, " \t").countTokens();
		}
		setStatusCode(0);
		wordCount = "" + noOfWords;
		return wordCount;
	}

	/**
	 * Calculate the number of lines in a file or from user input
	 * 
	 * @param input
	 *            - A string representation of the user input
	 * @return A string representation of the number of lines
	 */
	@Override
	public String getNewLineCount(String input) {
		setStatusCode(0);
		String[] strArray = input.split("\n");
		return "" + strArray.length;
	}

	/**
	 * Brief information about supported options
	 * 
	 * @return A string representation of the help message
	 */
	@Override
	public String getHelp() {
		setStatusCode(0);
		return "-m : Print only the character counts\t"
				+ " -w : Print only the word counts\t"
				+ " -l : Print only the newline counts\t"
				+ " -help : Brief information about supported options";
	}

	/**
	 * An execute method used by other classes
	 * 
	 * @param workingDir
	 *            - The working directory where the files are storing stdin -
	 *            The standard input from users
	 * @return A string representation of the resulting outcome or error message
	 *         returned from the related method
	 */
	@Override
	public String execute(File workingDir, String stdin) {
		if (args != null && args.length != 0) {
			String result = null;
			if (args.length == 1) {
				result = oneArg(args[0]);
			} else if (args.length == 2) {
				result = twoArgs(workingDir, stdin);
			} else if (args.length > 2) {
				result = moreThanTwoArgs(workingDir, stdin);
			}
			return result;
		} else {
			setStatusCode(1);
			return "Wrong command.";
		}
	}

	/**
	 * A method to be executed when there is only 1 argument
	 * 
	 * @param cmd
	 *            - A string representation of the only argument
	 * @return A string representation of the resulting outcome or error message
	 *         returned from the related method
	 */
	private String oneArg(String cmd) {
		String result = null;
		if (cmd == null){
			setStatusCode(1);
			result = "Wrong command.";
		}
		else if (cmd.equals("-help")) {
			result = getHelp();
		} else {
			setStatusCode(1);
			result = "Wrong command.";
		}
		return result;
	}

	/**
	 * A method to be executed when there is only 2 arguments
	 * 
	 * @param workingDir
	 *            - The working directory where the files are storing stdin -
	 *            The standard input from users
	 * @return A string representation of the resulting outcome or error message
	 *         returned from the related method
	 */
	private String twoArgs(File workingDir, String stdin) {
		String result = null;
		String secArg = args[1];
		if (secArg == null || secArg.equals("")) {
			setStatusCode(1);			
			result = "The file name is empty.";
		}
		else if (secArg.equals("-")) {
			result = readCountByType(args[0], stdin);
		} else {
			result = countFromFile(workingDir, secArg);
		}
		return result;
	}

	/**
	 * A method to be executed when there is more than 2 arguments
	 * 
	 * @param workingDir
	 *            - The working directory where the files are storing
	 * @return A string representation of the resulting outcome or error message
	 *         returned from the related method
	 */
	private String moreThanTwoArgs(File workingDir, String stdin) {
		String result = "";
		for (int i = 1; i < args.length; i++) {
			String fileName = args[i];
			if (isExisting(workingDir, fileName)) {
				result += countFromFile(workingDir, fileName) + "\n";
			} else if (fileName.equals("-")) {
				result += readCountByType(args[0], stdin) + "\n";
			} else {
				setStatusCode(1);
				String filePathWithName = workingDir.toString() + "\\"
						+ fileName + " not found.";
				result = filePathWithName;
				break;
			}
		}
		return result.trim();
	}

	/**
	 * A method to be called when there is a file name specified in arguments
	 * 
	 * @param workingDir
	 *            - The working directory where the files are storing fileName -
	 *            A string representation of the file name
	 * @return A string representation of the resulting outcome or error message
	 *         returned from the related method
	 */
	private String countFromFile(File workingDir, String fileName) {
		String result = null;
		if (fileName.equals("") || fileName == null) {
			result = "The file name is empty.";
		} else if (isExisting(workingDir, fileName)) {
			String content = readContentFromFile(workingDir, fileName);
			result = readCountByType(args[0], content);
		} else {
			setStatusCode(1);
			String filePathWithName = workingDir.toString() + "\\" + fileName
					+ " not found.";
			result = filePathWithName;
		}
		return result;
	}

	/**
	 * A method to be called to check which type of counting is going to be
	 * called and then call
	 * 
	 * @param type
	 *            - type must be either "-m" or "-w" or "-l" content - A string
	 *            to be counted
	 * @return A string representation of the resulting outcome or error message
	 *         returned from the related method
	 */
	private String readCountByType(String type, String content) {
		if (type.equals("-m")) {
			return getCharacterCount(content);
		} else if (type.equals("-w")) {
			return getWordCount(content);
		} else if (type.equals("-l")) {
			return getNewLineCount(content);
		} else {
			setStatusCode(1);
			return "Wrong command.";
		}
	}

	/**
	 * A method to check if a file is existing in the working directory
	 * 
	 * @param workingDir
	 *            - The working directory where the files are storing fileName -
	 *            A string representation of the file name
	 * @return A boolean representation if the file is existing
	 */
	private boolean isExisting(File workingDir, String fileName) {
		File f = new File(workingDir, fileName);
		if (f.exists()) {
			return true;
		} else {
			return false;
		}
	}

	/**
	 * A method to be called when reading the contents of a specified file
	 * 
	 * @param workingDir
	 *            - The working directory where the files are storing fileName -
	 *            A string representation of the file name
	 * @return A string representation of the contents of the file or error
	 *         message
	 */
	private static String readContentFromFile(File workingDir, String fileName) {
		String content = "";
		Scanner in = null;
		try {
			File f = new File(workingDir, fileName);
			in = new Scanner(f);
			while (in.hasNextLine()) {
				content = content + in.nextLine() + "\n";
				// content = content + in.nextLine();
			}

		} catch (FileNotFoundException e) {
			content = fileName + " not found.";
		} finally {
			in.close();
		}
		// return content.trim();
		return content;
	}
}